﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Component;
using Component.Mesh;
using DefaultNamespace;
using ExtraFoundation.Components;
using TMPro.EditorUtilities;
using UnityEngine;

public class DrawMeshTools : MonoBehaviour
{
   protected Mesh mesh;
   private MeshCollider meshCollider;
   
   protected Vector3[] vertices;
   protected int[] triangles;
   
   protected Color[] Colors;

   private List<Vector3> verticesList;
   private List<int> trianglesList;
   
   private Vector2 circleCenter;
   private Vector2 endPoint;
   private int currentCount;

   private int circleCount = 10;

   [SerializeField] private int sortLayer;
   [SerializeField] private bool meshHaveCollider;
   [SerializeField] private bool convex;
   protected LineData lineData;
   public LineData LineData => lineData;

   public bool Convex
   {
      get => convex;
      set => convex = value;
   }

   public bool MeshHaveCollider
   {
      get => meshHaveCollider;
      set => meshHaveCollider = value;
   }

   public int SortLayer
   {
      set => sortLayer = value;
   }

   public enum MeshType
   {
      Quad,
      Triangle,
      Round,
      Line,
   }

   public delegate void OnEnableAction();

   public OnEnableAction EnableAction { get; set; } 
   
   
   private void OnEnable()
   {
      EnableAction?.Invoke();
   }

   
   public static Dictionary<MeshType, int> CreateMeshTypes()
   {
      var dictionary = new Dictionary<MeshType, int>
      {
         {MeshType.Quad, 0}, {MeshType.Triangle, 0}, {MeshType.Round, 0}
      };
      return dictionary;
   }

   private void Awake()
   {
      gameObject.AddComponent<MeshRenderer>().material = Resources.Load<Material>("Material/default-mat");
      
      mesh = gameObject.AddComponent<MeshFilter>().mesh;
   }

   protected virtual void Start()
   {
      triangles = new int[0];
      vertices = new Vector3[0];
      GetComponent<MeshRenderer>().sortingOrder = sortLayer;
      if (meshHaveCollider)
      {
         meshCollider = gameObject.AddComponent<MeshCollider>();
         meshCollider.convex = convex;
      }
   }

   /// <summary>
   /// 节点动画
   /// </summary>
   /// <param name="type">节点类型</param>
   /// <param name="center">中心</param>
   /// <param name="startLength">初始边长</param>
   /// <param name="maxLength">最大边长</param>
   /// <param name="color">变化颜色</param>
   /// <param name="targetLength">目标长度</param>
   /// <returns></returns>
   public virtual IEnumerator OnClickMeshAnim(MeshType type, Vector2 center, float startLength, float maxLength, Color color, float targetLength = 0)
   {
      float sideDelta = (maxLength - startLength) / .5f * Time.fixedDeltaTime;
      float currentLength = startLength;
      bool holdMesh = false;
      int holdTimes = 0;
      while (true)
      {
         DrawMesh(type, center, currentLength, color);
         
         yield return new WaitForSeconds(Time.deltaTime);
         if (currentLength <= maxLength)
         {
            currentLength += sideDelta;
            
            if (Math.Abs(currentLength - maxLength) < 0.3f)
            {
               if(targetLength == 0)
                  yield break;
               sideDelta = (maxLength - targetLength) / .5f * Time.fixedDeltaTime;
               maxLength = targetLength;
            }
         }
         else
         {
            if (holdMesh && holdTimes < 2)
            {
               holdTimes++;
               sideDelta *= -1;
            }
            
            currentLength -= sideDelta;
            if (Math.Abs(currentLength - maxLength) < 0.1f)
            {
               holdMesh = true;
               sideDelta /= 2;
            }
         }
      }
   }

   public void DrawLine(Vector2 startPos, Vector2 endPos, float width, Color color)
   {

      MeshData meshData = DrawLineHelper(startPos, endPos, width);
      vertices = meshData.Vertices;
      triangles = meshData.Triangles;
      
      lineData = SimplifyMesh(vertices);
      CreateMesh(color);
   }

   /// <summary>
   /// 将点归一到第一象限
   /// </summary>
   /// <param name="original">原点</param>
   /// <param name="point">点</param>
   /// <param name="direction">单位象限值</param>
   private Vector2 NormalPoint(Vector2 original, Vector2 point, Vector2 direction)
   {      
      float newEndPosX = point.x, newEndPosY = point.y;

      if (direction.x < 0)
      {
         newEndPosX = -point.x + 2 * original.x;
      }
      if (direction.y < 0)
      {
         newEndPosY = -point.y + 2 * original.y;
      }
      return new Vector2(newEndPosX, newEndPosY);
   }

   /// <summary>
   /// 将点转换到正确的象限
   /// </summary>
   /// <param name="original"></param>
   /// <param name="points">转换点集</param>
   /// <param name="direction">单位象限值</param>
   private void NormalPoints(Vector2 original, IList<Vector3> points, Vector2 direction)
   {
      for (int i = 0; i < points.Count; i++)
      {
         float newEndPosX = points[i].x, newEndPosY = points[i].y;
         if (direction.x < 0)
         {
            newEndPosX = -points[i].x + 2 * original.x;
         }
         if (direction.y < 0)
         {
            newEndPosY = -points[i].y + 2 * original.y;
         }
         points[i] = new Vector2(newEndPosX, newEndPosY);
      }
   }
   
   /// <summary>
   /// 45°斜直线
   /// </summary>
   /// <param name="startPos">起始点</param>
   /// <param name="endPos">终止点</param>
   /// <param name="width">宽度</param>
   /// <param name="color">颜色</param>
   public void Draw45Line(Vector2 startPos, Vector2 endPos, float width, Color color)
   {
      Vector2 direction = (endPos - startPos).normalized;
      
      Vector2 normalPoint = NormalPoint(startPos, endPos, direction);
      normalPoint = new Vector2(normalPoint.x, startPos.y - startPos.x + normalPoint.x);
      
      vertices = new[]
      {
         new Vector3(normalPoint.x - width / 2 / Mathf.Sqrt(2), normalPoint.y + width / 2 / Mathf.Sqrt(2)), 
         new Vector3(normalPoint.x + width / 2 / Mathf.Sqrt(2), normalPoint.y - width / 2 / Mathf.Sqrt(2)),
         (Vector3)startPos + Vector3.right * width/2 * Mathf.Sqrt(2),
         (Vector3)startPos - Vector3.right * width/2 * Mathf.Sqrt(2),
      };

      triangles = new[]
      {
         0, 1, 2,
         2, 3, 0
      };
      NormalPoints(startPos, vertices, direction);

      lineData = SimplifyMesh(vertices);
      
      CreateMesh(color);
      
   }
   
   /// <summary>
   /// 45°转弯线
   /// </summary>
   /// <param name="startPos">起始点</param>
   /// <param name="endPos">终止点</param>
   /// <param name="width">宽度</param>
   /// <param name="color">颜色</param>
   public void Draw45TurnLine(Vector2 startPos, Vector2 endPos, float width, Color color)
   {
      Vector2 reflectEndPos = endPos; 
      
      Vector2 direction = (reflectEndPos - startPos).normalized;
      
      float newEndPosX = reflectEndPos.x, newEndPosY = reflectEndPos.y;

      if (direction.x < 0)
      {
         newEndPosX = -reflectEndPos.x + 2 * startPos.x;
      }
      if (direction.y < 0)
      {
         newEndPosY = -reflectEndPos.y + 2 * startPos.y;
      }
      
      reflectEndPos = new Vector2(newEndPosX, newEndPosY);  

      float currentAngle = 0;
      
      float pointsAngle = Mathf.Atan2(reflectEndPos.y - startPos.y, reflectEndPos.x - startPos.x) * Mathf.Rad2Deg;

      if (pointsAngle < .2f || Mathf.Abs(pointsAngle - 90) < .2f)
      {
         DrawLine(startPos, endPos, width, color);
         return;
      }
      
      if (Math.Abs(pointsAngle - 45) < .5f)
      {
         Draw45Line(startPos, endPos, width, color);
         return;
      }
      
      bool goUp = pointsAngle > 45;
      Vector2 turnDirection;
      //获取转弯圆心、方向、角度
      if (goUp)
      {
         circleCenter = new Vector2(reflectEndPos.x - width / 2 * 3, reflectEndPos.y);
         turnDirection = new Vector2(-1,-1);
         currentAngle = 0;
      }
      else
      {
         circleCenter = new Vector2(reflectEndPos.x, reflectEndPos.y - width / 2 * 3);
         turnDirection = new Vector2(-1, 1);
         currentAngle = 90;
      }

      MeshData turnData = DrawLineTurn(2 * width, width, circleCenter, turnDirection, currentAngle, goUp);

      Vector2 p45 = (turnData.LastPoint1 + turnData.LastPoint2) / 2;
      
      //获取正确的点位置
      if (goUp)
      {
         float dy = (p45.x - startPos.x) + startPos.y - p45.y;
         for (int i = 0; i < turnData.Vertices.Length; i++)
         {
            turnData.Vertices[i].y += dy;
         }
      }
      else
      {
         float dx = (p45.y - startPos.y) - p45.x + startPos.x;
         for (int i = 0; i < turnData.Vertices.Length; i++)
         {
            turnData.Vertices[i].x += dx;
         }
      }

      //顶点、顶点数初始化
      List<Vector3> vList = new List<Vector3>();
      List<int> tList = new List<int>();

      if (goUp)
      {
         vList.Add(reflectEndPos + Vector2.right * width/2);
         vList.Add(reflectEndPos + Vector2.left * width/2);
      }
      else
      {
         vList.Add(reflectEndPos + Vector2.up * width/2);
         vList.Add(reflectEndPos - Vector2.up * width/2);
      }
      vList.AddRange(turnData.Vertices.ToList());
      vList.Add(startPos + Vector2.right * width/2 * Mathf.Sqrt(2));
      vList.Add(startPos - Vector2.right * width/2 * Mathf.Sqrt(2));

      int[] t;
      int vLength = vList.Count;
      
      //直线部分
      if (goUp)
      {
         t = new[]
         {
            2,1,0,
            2,3,1
         };
      }
      else
      {
         t = new[]
         {
            0,1,2,
            1,3,2
         };
      }

      tList.AddRange(t.ToArray());
      //转弯部分
      tList.AddRange(turnData.Triangles.Select(triangle => triangle + 2));
      //斜线
      if (goUp)
      {
         t = new[]
         {
            vLength - 4, vLength - 1, vLength - 3,
            vLength - 4, vLength - 2, vLength - 1,
         };
      }
      else
      {
         t = new[]
         {
            vLength - 4, vLength - 3, vLength - 2,
            vLength - 4, vLength - 2, vLength - 1,
         };
      }
      tList.AddRange(t.ToArray());

      //在第一象限变化完后，要变换回正确的象限
      Vector3[] temp = new Vector3[vList.Count];
      for (int i = 0; i < temp.Length; i++)
      {
         var targetVerticesX = vList[i].x;
         var targetVerticesY = vList[i].y;

         if (direction.x < 0)
         {
            targetVerticesX = -vList[i].x + 2 * startPos.x;
         }

         if (direction.y < 0)
         {
            targetVerticesY = -vList[i].y + 2 * startPos.y;
         }

         temp[i] = new Vector3(targetVerticesX, targetVerticesY);
      }
      
      if (direction.x * direction.y < 0)
      {
         triangles = new int[tList.Count];
         for (int i = 0; i < triangles.Length; i++)
         {
            triangles[i] = tList[triangles.Length - i - 1];
         }
      }
      else
      {
         triangles = tList.ToArray();
      }
      
      
      vertices = temp;

      CreateMesh(color);
   }

   private MeshData DrawLineTurn(float radius, float innerRadius, Vector2 centerCircle, Vector2 direction, float currentAngle, bool reverse = false)
   {
      int segments = 10;
      segments++;
      currentAngle = currentAngle * Mathf.Deg2Rad;
      
      //顶点
      Vector3[] vertices = new Vector3[segments * 2];
      float deltaAngle = Mathf.Deg2Rad * (45 + 45f/segments) / segments;
      
      for (int i = 0; i < vertices.Length; i += 2)
      {
         float cosA = Mathf.Cos(currentAngle);
         float sinA = Mathf.Sin(currentAngle);
         // vertices[i] = new Vector3(cosA * innerRadius + centerCircle.x, sinA * innerRadius + centerCircle.y, 0);
         // vertices[i + 1] = new Vector3(cosA * radius + centerCircle.x, sinA * radius + centerCircle.y, 0);
         vertices[i] = new Vector3(cosA * radius + centerCircle.x, sinA * radius + centerCircle.y, 0);
         vertices[i + 1] = new Vector3(cosA * innerRadius + centerCircle.x, sinA * innerRadius + centerCircle.y, 0);
         currentAngle -= (direction.x * direction.y) * deltaAngle;
      }
 
      //三角形
      int[] triangles = new int[(segments - 1) * 6];
      for (int i = 0, j = 0; i < triangles.Length; i += 6, j += 2)
      {
         triangles[i] = j;
         triangles[i + 1] = (j + 1) % vertices.Length;
         triangles[i + 2] = (j + 3) % vertices.Length;
 
         triangles[i + 3] = j;
         triangles[i + 4] = (j + 3) % vertices.Length;
         triangles[i + 5] = (j + 2) % vertices.Length;
      }

      if (reverse)
      {
         int[] t = new int[triangles.Length];
         for (int i = 0; i < triangles.Length; i++)
         {
            t[triangles.Length - i - 1] = triangles[i];
         }
         // vertices = v;
         triangles = t;
      }
      
      return new MeshData(){Vertices = vertices, Triangles = triangles};
   }

   /// <summary>
   /// 画圆环
   /// </summary>
   /// <param name="radius">圆半径</param>
   /// <param name="innerRadius">内圆半径</param>
   /// <param name="segments">圆的分个数</param>
   /// <param name="centerCircle">圆心坐标</param>
   /// <param name="angle">圆角</param>
   private void DrawRingHelper(float radius, float innerRadius, int segments, Vector3 centerCircle, float angle = 360)
   {
      //顶点
      vertices = new Vector3[segments * 2];
      float deltaAngle = Mathf.Deg2Rad * (angle + angle/segments) / segments;
      float currentAngle = 90 * Mathf.Deg2Rad;
      for (int i = 0; i < vertices.Length; i += 2)
      {
         float cosA = Mathf.Cos(currentAngle);
         float sinA = Mathf.Sin(currentAngle);
         vertices[i] = new Vector3(cosA * innerRadius + centerCircle.x, sinA * innerRadius + centerCircle.y, 0);
         vertices[i + 1] = new Vector3(cosA * radius + centerCircle.x, sinA * radius + centerCircle.y, 0);
         currentAngle -= deltaAngle;
      }
 
      //三角形
      if (Math.Abs(angle - 360) < Mathf.Epsilon)
      {
         triangles = new int[segments * 6];
      }
      else
      {
         triangles = new int[(segments - 1) * 6];
      }
      for (int i = 0, j = 0; i < triangles.Length; i += 6, j += 2)
      {
         triangles[i] = j;
         triangles[i + 1] = (j + 1) % vertices.Length;
         triangles[i + 2] = (j + 3) % vertices.Length;
 
         triangles[i + 3] = j;
         triangles[i + 4] = (j + 3) % vertices.Length;
         triangles[i + 5] = (j + 2) % vertices.Length;
      }
   }
   public void DrawRing(float radius, float innerRadius, int segments, Vector3 centerCircle, Color color, float angle = 360)
   {
      DrawRingHelper(radius, innerRadius, segments, centerCircle, angle);
      CreateMesh(color);
   }
   
   
   /// <summary>
   /// 小火车绘制
   /// </summary>
   /// <param name="center"></param>
   /// <param name="width"></param>
   /// <param name="height"></param>
   /// <param name="color"></param>
   public void DrawTrain(Vector2 center, float width, float height,Color color)
   {
      vertices = new[]
      {
         (Vector3)center,
         new Vector3(center.x - height/2, center.y + width/2), 
         new Vector3(center.x + height/2, center.y + width/2), 
         new Vector3(center.x + height/2, center.y - width/2), 
         new Vector3(center.x - height/2, center.y - width/2),
         new Vector3(center.x + height/10 * 7, center.y), 
      };

      triangles = new[]
      {
         0,1,2,
         0,4,1,
         0,3,4,
         2,5,3,
         0,2,3
      };
      
      CreateMesh(color);
   }
   
   /// <summary>
   /// 通过图形绘制
   /// </summary>
   /// <param name="type">绘制形状</param>
   /// <param name="center">中心</param>
   /// <param name="sideLength">边长</param>
   /// <param name="color">颜色</param>
   public void DrawMesh(MeshType type, Vector2 center, float sideLength, Color color)
   {
      float radius = sideLength / 2;

         switch (type)
         {
            case MeshType.Quad:
               vertices = new Vector3[]{
                  center,
                  new Vector2(center.x - radius,center.y + radius), 
                  new Vector2(center.x + radius,center.y + radius), 
                  new Vector2(center.x + radius,center.y - radius), 
                  new Vector2(center.x - radius,center.y - radius), 
               };
        
               triangles = new int[]
               {
                  0,1,2,
                  0,2,3,
                  0,3,4,
                  0,4,1
               };
               break;
            case MeshType.Round:
               DrawRingHelper(radius, 0, 180, center);
               break;
            case MeshType.Triangle:
               //       p3
               //       
               //   p2      p1
               vertices = new[]
               {
                  new Vector3(center.x + sideLength / 2, center.y - sideLength / Mathf.Sqrt(3) / 2), 
                  new Vector3(center.x - sideLength / 2, center.y - sideLength / Mathf.Sqrt(3)/ 2), 
                  new Vector3(center.x, center.y + sideLength / Mathf.Sqrt(3)),
               };
               triangles = new[]
               {
                  0,1,2
               };
               break;
            default:
               print(gameObject.name + "type is error");
               break;
         }

         CreateMesh(color);
   }
   
   /**
    * 三角形，
    */
   public void DrawTriangle(Vector2 center, float sideLength, Color color)
   {
      vertices = new[]
      {
         new Vector3(center.x + sideLength * Mathf.Sqrt(3) / 3, center.y - sideLength / 2),
         new Vector3(center.x - sideLength * Mathf.Sqrt(3) / 3, center.y - sideLength / 2),
         new Vector3(center.x, center.y + sideLength / 2),
      };
      triangles = new[]
      {
         0, 1, 2
      };
      CreateMesh(color);
   }
   
   private MeshData DrawLineHelper(Vector2 startPos, Vector2 endPos, float width)
   {
      Vector3[] v;
      int[] t;
      if (Mathf.Abs(startPos.y - endPos.y) > Mathf.Abs(startPos.x - endPos.x))
      {
         v = new Vector3[]
         {
            new Vector2(startPos.x - width/2, endPos.y),
            new Vector2(startPos.x + width/2, endPos.y),
            new Vector2(startPos.x - width/2, startPos.y),
            new Vector2(startPos.x + width/2, startPos.y),
         };
         if (startPos.y > endPos.y)
         {
            t = new[]
            {
               0, 2, 3,
               0, 3, 1
            };  
         }
         else
         {
            t = new[]
            {
               3, 2, 0,
               1, 3, 0
            };  
         }
      }
      else
      {
         v = new Vector3[]
         {
            new Vector2(endPos.x, endPos.y +  width/2),
            new Vector2(endPos.x, endPos.y -  width/2),
            new Vector2(startPos.x, startPos.y + width/2),
            new Vector2(startPos.x, startPos.y - width/2),
         };
         if (startPos.x > endPos.x)
         {
            t = new[]
            {
               0, 2, 3,
               0, 3, 1
            };  
         }
         else
         {
            t = new[]
            {
               3, 2, 0,
               1, 3, 0
            };  
         }
      }
      
      
      
      return new MeshData(){Vertices = v,Triangles = t};
   }
   private MeshData DrawLine2(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4)
   {
      Vector3[] v = new[] {p1, p2, p3, p4};
        
      int[] t = 
      {
         1,2,0,
         2,3,0
      };
      
      return new MeshData(){Vertices = v,Triangles = t};
   }
   protected virtual void CreateMesh(Color color)
   {
      
      lineData = SimplifyMesh(vertices);
      lineData.LineColor = color;
      
      mesh.Clear();

      //我也不知道为什么减去transform.position
      for (int i = 0; i < vertices.Length; i++)
      {
         vertices[i] -= transform.position;
      }
      
      mesh.vertices = vertices;
      mesh.triangles = triangles;
      
      Colors = new Color[vertices.Length];
      for (int i = 0; i < Colors.Length; i++)
      {
         Colors[i] = color;
      }

      mesh.colors = Colors;
      mesh.RecalculateNormals();

      if(meshCollider != null)
         meshCollider.sharedMesh = mesh;
   }
   
   public void ClearMesh()
   {
      mesh.Clear();
   }

   public struct MeshData
   {
      public Vector3[] Vertices;
      public int[] Triangles;

      public Vector2 FirstPoint1 => Vertices[1];
      public Vector2 FirstPoint2 => Vertices[0];
      
      public Vector2 LastPoint1 => Vertices[Vertices.Length - 1];
      public Vector2 LastPoint2 => Vertices[Vertices.Length - 2];
   }

   public LineData SimplifyMesh(Vector3[] vertices)
   {
      Vector2[] points = new Vector2[vertices.Length/2];
      
      for (int i = 1; i < vertices.Length; i += 2)
      {
         Vector3 temp = (vertices[i - 1] + vertices[i]) / 2;
         points[(i - 1) / 2] = temp;
      }
      
      foreach (var point in points)
      {
         Debug.DrawRay(point, Vector3.up);
      }
      
      return new LineData(points, points[0],(vertices[vertices.Length/2] + vertices[vertices.Length/2 + 1])/2);
   }
   
}
